package dynamic.wrapper.resultmap;

import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.github.yulichang.wrapper.resultmap.IResult;
import com.github.yulichang.wrapper.resultmap.MFunc;
import dynamic.metadata.DTableInfo;
import dynamic.metadata.EntityClass;
import dynamic.metadata.EntityProperty;
import dynamic.mybatisplus.core.toolkit.DAsserts;
import dynamic.mybatisplus.core.toolkit.DTableHelper;
import dynamic.toolkit.support.DColumnCache;
import dynamic.wrapper.segments.DSelectCache;
import lombok.Getter;

import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * collection 标签 目前先支持这几个属性 后续在扩展
 *
 * @author yulichang
 * @since 1.3.0
 */

public class DMybatisLabel implements DLabel {

    private String index;

    private String property;

    private EntityClass entityClass;

    private Class<?> javaType;

    private EntityClass ofType;

    private List<DIResult> resultList;

    /**
     * wrapper里面的引用
     */
    private List<DLabel> mybatisLabels;

    private DMybatisLabel() {
    }

    @Override
    public String getProperty() {
        return property;
    }

    @Override
    public Class<?> getJavaType() {
        return javaType;
    }

    @Override
    public EntityClass getOfType() {
        return ofType;
    }

    @Override
    public List<DIResult> getResultList() {
        return resultList;
    }

    @Override
    public List<DLabel> getMybatisLabels() {
        return mybatisLabels;
    }

    @SuppressWarnings({"unused", "unchecked", "DuplicatedCode"})
    public static class Builder {

        private final DMybatisLabel mybatisLabel;

        /**
         * 自动构建
         */
        @SuppressWarnings("unchecked")
        public Builder(String index, String property, EntityClass entityClass, Class<?> javaType) {
            this.mybatisLabel = new DMybatisLabel();
            mybatisLabel.property = property;
            mybatisLabel.index = index;
            mybatisLabel.entityClass = entityClass;
            mybatisLabel.javaType = javaType;
            mybatisLabel.ofType =  entityClass;
            mybatisLabel.resultList = new ArrayList<>();
            mybatisLabel.mybatisLabels = new ArrayList<>();
            autoBuild(true, entityClass, entityClass);
        }

        /**
         * 手动构建
         *
         * @param property    property
         * @param entityClass 数据库实体类
         * @param javaType    javaType
         * @param ofType      映射类
         * @param auto        自动映射数据库实体对应的字段
         */
        public Builder(String index, String property, EntityClass entityClass, Class<?> javaType, EntityClass ofType, boolean auto) {
            this.mybatisLabel = new DMybatisLabel();
            mybatisLabel.property = property;
            mybatisLabel.index = index;
            mybatisLabel.entityClass = entityClass;
            mybatisLabel.javaType = javaType;
            mybatisLabel.ofType = ofType;
            mybatisLabel.resultList = new ArrayList<>();
            mybatisLabel.mybatisLabels = new ArrayList<>();
            autoBuild(auto, entityClass, ofType);
        }

        /**
         * 映射实体全部字段
         */
        public Builder all() {
            autoBuild(true, mybatisLabel.entityClass, mybatisLabel.ofType);
            return this;
        }

        /**
         * 映射实体字段过滤(含主键)
         */
        public Builder filter(Predicate<DSelectCache> predicate) {
            Map<String, EntityProperty> fieldMap = mybatisLabel.ofType.getProperties();//MPJReflectionKit.getFieldMap(mybatisLabel.ofType);
            DColumnCache.getListField(mybatisLabel.entityClass).stream().filter(predicate)
                    .filter(p -> fieldMap.containsKey(p.getColumProperty())).forEach(c ->
                            mybatisLabel.resultList.add(new DResult.Builder(c.isPk(), mybatisLabel.index, c).build()));
            return this;
        }

        public Builder id(EntityProperty entity, EntityProperty tag) {
            DResult.Builder builder = new DResult.Builder(true, mybatisLabel.index);
            builder.column(entity).property(tag);
            mybatisLabel.resultList.add(builder.build());
            return this;
        }

        public Builder id(EntityProperty entity) {
            DResult.Builder builder = new DResult.Builder(true, mybatisLabel.index);
            builder.column(entity);
            mybatisLabel.resultList.add(builder.build());
            return this;
        }

        public Builder result(EntityProperty entity, EntityProperty tag) {
            DResult.Builder builder = new DResult.Builder(false, mybatisLabel.index);
            builder.column(entity).property(tag);
            mybatisLabel.resultList.add(builder.build());
            return this;
        }

        public Builder result(EntityProperty entity) {
            DResult.Builder builder = new DResult.Builder(false, mybatisLabel.index);
            builder.column(entity);
            mybatisLabel.resultList.add(builder.build());
            return this;
        }

        public <A, R, B extends Collection<R>> Builder collection(EntityClass entityClass,EntityProperty func) {
            return collection(null, entityClass, func);
        }

        /**
         * 嵌套
         */
        public <A, R, B extends Collection<R>> Builder collection(String prefix, EntityClass entityClass, EntityProperty func) {
            String dtoFieldName = func.getPropertyName(); //LambdaUtils.getName(func);
//            Class dtoClass = LambdaUtils.getEntityClass(func);
//            Map<String, FieldCache> fieldMap = MPJReflectionKit.getFieldMap(dtoClass);
//            FieldCache field = fieldMap.get(dtoFieldName);
//            Class<?> genericType = MPJReflectionKit.getGenericType(field.getField());
            EntityClass genericType = func.getGenericType();
            Builder builder;
            if (genericType == null || genericType.isAssignableFrom(entityClass)) {
                //找不到集合泛型 List List<?> List<Object> ， 直接查询数据库实体
                builder = new Builder(prefix, dtoFieldName, entityClass, func.getPropertyClass());
            } else {
                //Class<R> ofType = (Class<R>) genericType;
                builder = new Builder(prefix, dtoFieldName, entityClass, func.getPropertyClass(), func.getGenericType(), true);
            }
            mybatisLabel.mybatisLabels.add(builder.build());
            return this;
        }

        public <A, R, B extends Collection<R>> Builder collection(EntityClass entityClass,EntityProperty func, MFunc<Builder> mFunc) {
            return collection(null, entityClass, func, mFunc);
        }

        /**
         * 嵌套
         */
        public <A, R, B extends Collection<R>> Builder collection(EntityProperty func,
                                                                        MFunc<DMybatisLabelFree.Builder> mFunc) {
            String dtoFieldName = func.getPropertyName();//LambdaUtils.getName(func);
//            Class dtoClass = LambdaUtils.getEntityClass(func);
//            FieldCache field = MPJReflectionKit.getFieldMap(dtoClass).get(dtoFieldName);
            //获取集合泛型
            EntityClass genericType = func.getGenericType();//MPJReflectionKit.getGenericType(field.getField());
            //Class<R> ofType = (Class<R>) genericType;
            DMybatisLabelFree.Builder builder = new DMybatisLabelFree.Builder(dtoFieldName, func.getPropertyClass(), func.getGenericType());
            mybatisLabel.mybatisLabels.add(mFunc.apply(builder).build());
            return this;
        }

        /**
         * 嵌套
         */
        public <A, R, B extends Collection<R>> Builder collection(String prefix,
                                                                        EntityClass entityClass,
                                                                        EntityProperty func,
                                                                        MFunc<Builder> mFunc) {
            String dtoFieldName =  func.getPropertyName();//LambdaUtils.getName(func);
//            Class dtoClass = LambdaUtils.getEntityClass(func);
//            FieldCache field = MPJReflectionKit.getFieldMap(dtoClass).get(dtoFieldName);
//            //获取集合泛型
//            Class<?> genericType = MPJReflectionKit.getGenericType(field.getField());
            EntityClass ofType = func.getGenericType();
            Builder builder = new Builder(prefix, dtoFieldName, entityClass, func.getPropertyClass(), ofType, false);
            mybatisLabel.mybatisLabels.add(mFunc.apply(builder).build());
            return this;
        }

        public <A, B> Builder association(EntityClass child, EntityProperty dtoField) {
            return association(null, child, dtoField);
        }

        /**
         * 嵌套
         */
        public <A, B> Builder association(String index,EntityClass child, EntityProperty dtoField) {
//            Class dtoClass = LambdaUtils.getEntityClass(dtoField);
//            Map<String, FieldCache> fieldMap = MPJReflectionKit.getFieldMap(dtoClass);
//            String dtoFieldName = LambdaUtils.getName(dtoField);
            //FieldCache field = fieldMap.get(dtoFieldName);
            Assert.isFalse(Collection.class.isAssignableFrom(dtoField.getPropertyClass()), "association 不支持集合类");
            Builder builder;
            builder = new Builder(index, dtoField.getPropertyName(), child, dtoField.getPropertyClass(), dtoField.getGenericType(), true);
            mybatisLabel.mybatisLabels.add(builder.build());
            return this;
        }

        public <A, B> Builder association(EntityClass child, EntityProperty dtoField,
                                                MFunc<DMybatisLabel.Builder> collection) {
            return association(null, child, dtoField, collection);
        }

        /**
         * 嵌套
         */
        public <A, B> Builder association(EntityProperty dtoField,
                                                MFunc<DMybatisLabelFree.Builder> collection) {
            String dtoFieldName = dtoField.getPropertyName();//LambdaUtils.getName(dtoField);
//            Class dtoClass = LambdaUtils.getEntityClass(dtoField);
//            FieldCache field = MPJReflectionKit.getFieldMap(dtoClass).get(dtoFieldName);
            Assert.isFalse(Collection.class.isAssignableFrom(dtoField.getPropertyClass()), "association 不支持集合类");
            DMybatisLabelFree.Builder builder = new DMybatisLabelFree.Builder(dtoFieldName, dtoField.getPropertyClass(), dtoField.getGenericType());
            mybatisLabel.mybatisLabels.add(collection.apply(builder).build());
            return this;
        }

        /**
         * 嵌套
         */
        public <A, B> Builder association(String index, EntityClass child, EntityProperty dtoField,
                                                MFunc<Builder> collection) {
            String dtoFieldName =  dtoField.getPropertyName();//DColumnCache.getName(dtoField);
//            EntityClass dtoClass = dtoField.getEntityClass();
//            FieldCache field = MPJReflectionKit.getFieldMap(dtoClass).get(dtoFieldName);
            Assert.isFalse(Collection.class.isAssignableFrom(dtoField.getPropertyClass()), "association 不支持集合类");
            Builder builder = new Builder(index, dtoFieldName, child, dtoField.getPropertyClass(), dtoField.getGenericType(), false);
            mybatisLabel.mybatisLabels.add(collection.apply(builder).build());
            return this;
        }

        public DMybatisLabel build() {
            if (CollectionUtils.isEmpty(mybatisLabel.resultList)) {
                autoBuild(true, mybatisLabel.entityClass, mybatisLabel.ofType);
            }
            return mybatisLabel;
        }

        private void autoBuild(boolean auto, EntityClass entityClass, EntityClass tagClass) {
            DTableInfo tableInfo = DTableHelper.get(entityClass);
            DAsserts.hasTable(tableInfo, entityClass);
            Map<String, EntityProperty> tagMap = tagClass.getProperties();
            if (auto && !tagMap.isEmpty()) {
                List<DSelectCache> listField = DColumnCache.getListField(entityClass);
                if (entityClass.isAssignableFrom(tagClass)) {
                    mybatisLabel.resultList.addAll(listField.stream().map(i -> {
                        DResult result = new DResult();
                        result.setId(i.isPk());
                        result.setIndex(mybatisLabel.index);
                        result.setProperty(i.getColumProperty());
                        result.setJavaType(i.getColumnType());
                        result.setJdbcType(Objects.isNull(i.getTableFieldInfo()) ? null : i.getTableFieldInfo().getJdbcType());
                        result.setSelectNormal(i);
                        return result;
                    }).collect(Collectors.toList()));
                } else {
                    for (DSelectCache s : listField) {
                        EntityProperty field = tagMap.get(s.getColumProperty());
                        if (Objects.nonNull(field)) {
                            DResult result = new DResult();
                            result.setId(s.isPk());
                            result.setIndex(mybatisLabel.index);
                            result.setProperty(s.getColumProperty());
                            result.setJavaType(field.getPropertyClass());
                            result.setSelectNormal(s);
                            mybatisLabel.resultList.add(result);
                        }
                    }
                }
            }
        }
    }
}
